using System;

namespace RikMigrations
{
    [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = true)]
    public class MigrationAttribute : Attribute, IComparable<MigrationAttribute>
    {
		
        #region [rgn] Fields (3)

        private string moduleName;
        private Type type;
        private int version;

        #endregion [rgn]

        #region [rgn] Constructors (3)

        public MigrationAttribute(Type type, int version, string moduleName)
        {
            this.type = type;
            this.version = version;
            this.moduleName = moduleName;
        }
		
        public MigrationAttribute(Type type, int version)
        {
            this.type = type;
            this.version = version;
        }
		
        /// <summary>
        /// Only use this on a class not on an Assembly Attribute
        /// </summary>
        /// <param name="version"></param>
        public MigrationAttribute(int version)
        {
            this.version = version;
        }
		
        #endregion [rgn]

        #region [rgn] Properties (3)

        public string ModuleName
        {
            get { return moduleName ?? MigrationManager.DefaultModuleName; }
            set { moduleName = value; }
        }
		
        public Type Type
        {
            get { return type; }
            set { type = value; }
        }
		
        public int Version
        {
            get { return version; }
        }
		
        #endregion [rgn]

        #region [rgn] Methods (3)

        // [rgn] Public Methods (2)

        public void Down(DbProvider db)
        {
            runMigration(db, delegate(IMigration migration)
                                 {
                                     Log.WriteLine("Downgrading from version {0} (Module: {1})", Version, ModuleName);
                                     migration.Down(db);
                                 });
        }
		
        public void Up(DbProvider db)
        {
            runMigration(db, delegate(IMigration migration)
                                 {
                                     Log.WriteLine("Upgrading to version {0} (Module: {1})", Version, ModuleName);
                                     migration.Up(db);
                                 });
        }
		
        // [rgn] Private Methods (1)

        private void runMigration(DbProvider db, Action<IMigration> migrationToRun)
        {
            db.CreateTransaction();
            try
            {
                migrationToRun.Invoke(createInstanceOfMigration());
                db.ExecuteCommands();
                db.CommitTransaction();
            }
            catch
            {
                db.RollbackTransaction();
                throw;
            }
        }

        private IMigration createInstanceOfMigration()
        {
            IMigration migration = Activator.CreateInstance(Type) as IMigration;
            if (migration == null)
                throw new InvalidOperationException("Migration type does not implement IMigration - " + Type.Name);
            return migration;
        }

        #endregion [rgn]

        #region IComparable<MigrationAttribute> Members
        public int CompareTo(MigrationAttribute that)
        {
            return version.CompareTo(that.version);
        }
        #endregion
    }
}